///
/// Copyright (C) 2016, Dependable Systems Laboratory, EPFL
/// Copyright (C) 2015-2016, Cyberhaven
/// All rights reserved.
///
/// Licensed under the Cyberhaven Research License Agreement.
///

#ifndef S2E_PLUGINS_PovGenerationPolicy_H
#define S2E_PLUGINS_PovGenerationPolicy_H

#include <s2e/CorePlugin.h>
#include <s2e/Plugin.h>
#include <s2e/Plugins/ExecutionTracers/TestCaseGenerator.h>
#include <s2e/Plugins/OSMonitors/Linux/LinuxMonitor.h>
#include <s2e/Plugins/OSMonitors/Support/ProcessExecutionDetector.h>
#include <s2e/Plugins/OSMonitors/Windows/WindowsCrashMonitor.h>
#include <s2e/Plugins/VulnerabilityAnalysis/DecreePovGenerator.h>
#include <s2e/S2EExecutionState.h>

namespace s2e {
namespace plugins {

namespace recipe {
class Recipe;
}

class DecreeMonitor;

class PovGenerationPolicy : public Plugin {
    S2E_PLUGIN

private:
    DecreeMonitor *m_decreeMonitor;
    LinuxMonitor *m_linuxMonitor;
    WindowsCrashMonitor *m_windowsCrashMonitor;

    recipe::Recipe *m_recipe;
    pov::PovGenerator *m_povGenerator;
    ProcessExecutionDetector *m_process;

    uint64_t m_maxCrashCount;
    uint64_t m_crashCount;

    typedef pov::PovOptions PovOptions;
    typedef pov::PovType PovType;

    typedef std::tuple<uint64_t /* faultAddr */, unsigned /* povType */, std::string /* recipeName */> UniquePovKey;
    typedef std::map<UniquePovKey, unsigned /* count */> UniquePovMap;

    // TODO: implement proper sharing between multiple s2e processes (e.g., through KeyValueStore plugin)
    UniquePovMap m_uniquePovMap;
    unsigned m_maxPovCount;

    void onPovReadyHandler(S2EExecutionState *state, const PovOptions &opt, const std::string &recipeName,
                           bool isCrash);
    void onSegFault(S2EExecutionState *state, uint64_t pid, uint64_t pc);

    void onSegFaultWinUser(S2EExecutionState *state, const WindowsUserModeCrash &crash);

    void onSegFaultWinKernel(S2EExecutionState *state, const vmi::windows::BugCheckDescription &crash);

    void onSymbolicAddress(S2EExecutionState *state, ref<Expr> virtualAddress, uint64_t concreteAddress,
                           bool &concretize, CorePlugin::symbolicAddressReason reason);

public:
    enum TestCaseType { POV, CRASH, END_OF_PATH, PARTIAL_PATH };

    void initialize();
    PovGenerationPolicy(S2E *s2e) : Plugin(s2e) {
    }

    sigc::signal<void, S2EExecutionState *, const PovOptions &, const std::string & /* recipeName */,
                 const std::vector<std::string> & /* filePaths */, TestCaseType /* tcType */>
        onPovReady;
};

} // namespace plugins
} // namespace s2e

#endif // S2E_PLUGINS_PovGenerationPolicy_H
